//Author: bishisimo
//Time: 2021/7/8 下午7:51
//Describe:
package console

import (
	"bytes"
	"encoding/json"
	"fmt"
	"gitee.com/bishisimo/log-parser/model"
	"io"
	"os"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
)

type LogParser interface {
	ParseLog(raw []byte) (*model.LogInfo, error)
}

var consoleBufPool = sync.Pool{
	New: func() interface{} {
		return bytes.NewBuffer(make([]byte, 0, 100))
	},
}

type ConsoleWriter struct {
	Out     io.Writer
	NoColor bool
	LogParser
}

func NewConsoleWriter(p LogParser) ConsoleWriter {
	return ConsoleWriter{
		Out:       os.Stdout,
		LogParser: p,
	}
}

/**
 * @Description: 实现Writer接口
 * @receiver w 控制体输出结构体
 * @param p 需要输出的原日志
 * @return n 行数
 * @return err 错误
 */
func (w ConsoleWriter) Write(raw []byte) (n int, err error) {
	//解析json，将interface的数字取消默认类型float64
	event, err := w.ParseLog(raw)
	if err != nil {
		return 0, err
	}
	//获取buffer流对象
	buf := consoleBufPool.Get().(*bytes.Buffer)
	defer consoleBufPool.Put(buf)
	//解析lever
	lvlColor := model.CReset
	level := "????"
	if event.Lever != "" {
		if !w.NoColor {
			lvlColor = levelColor(event.Lever)
		}
		level = strings.ToUpper(event.Lever)[0:4]
	}
	fmt.Fprintf(buf, "%s|%s|%s| %s |%s",
		model.Colorize(formatTime(event.Timestamp), model.CDarkGray, !w.NoColor),
		model.Colorize(level, lvlColor, !w.NoColor),
		model.Colorize(event.CallerName, model.CReset, !w.NoColor),
		model.Colorize(event.Location, model.CReset, !w.NoColor),
		model.Colorize(event.Msg, model.CReset, !w.NoColor))
	//解析自定义fields
	fields := make([]string, 0, len(event.Fields))
	for field := range event.Fields {
		fields = append(fields, field)
	}
	sort.Strings(fields)
	for _, field := range fields {
		fmt.Fprintf(buf, " %s=", model.Colorize(field, model.CCyan, !w.NoColor))
		switch value := event.Fields[field].(type) {
		case string:
			if needsQuote(value) {
				buf.WriteString(strconv.Quote(value))
			} else {
				buf.WriteString(value)
			}
		case json.Number:
			fmt.Fprint(buf, value)
		default:
			b, err := json.Marshal(value)
			if err != nil {
				fmt.Fprintf(buf, "[error: %v]", err)
			} else {
				fmt.Fprint(buf, string(b))
			}
		}
	}
	buf.WriteByte('\n')
	buf.WriteTo(w.Out)
	n = len(raw)
	return
}

func formatTime(t interface{}) string {
	switch t := t.(type) {
	case string:
		return t
	case json.Number:
		u, _ := t.Int64()
		return time.Unix(u, 0).Format(time.RFC3339)
	case int64:
		return time.Unix(t, 0).Format(time.RFC3339)
	default:
		return "<nil>"
	}
}

func levelColor(level string) int {
	switch strings.ToUpper(level) {
	case "TRAC", "TRACE":
		return model.CDarkGray
	case "DEBU", "DEBUG":
		return model.CGray
	case "INFO":
		return model.CGreen
	case "WARN":
		return model.CYellow
	case "ERRO", "ERROR", "FATA", "FATAL", "PANI", "PANIC":
		return model.CRed
	default:
		return model.CReset
	}
}

func needsQuote(s string) bool {
	for i := range s {
		if s[i] < 0x20 || s[i] > 0x7e || s[i] == ' ' || s[i] == '\\' || s[i] == '"' {
			return true
		}
	}
	return false
}
